/*
 * ASP.NET Validation Controls for MVC (using WebUIValidation.js)
 *
 * Copyright (c) 2009 Guramrit Singh Dhillon
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 * 
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.Mvc;
using System.Web.UI.WebControls;

namespace Classic.Mvc.Web.UI.Validators
{
    public abstract class BaseValidator : IMvcControl
    {
        private readonly string _controlToValidate;
        private readonly object _htmlAttributes;
        private readonly IDictionary<string, object> _scriptAttributes = new Dictionary<string, object>();

        protected BaseValidator(string id, string controlToValidate, object htmlAttributes)
        {
            ID = id;
            _controlToValidate = controlToValidate;
            _htmlAttributes = htmlAttributes;
            Display = ValidatorDisplay.Static;
            Enabled = true;
            ErrorElement = "span";
        }

        protected void RegisterScriptAttribute(string attributeName, string attributeValue, bool encode)
        {
            if (encode)
            {
                _scriptAttributes.Add(attributeName, "\"" + attributeValue + "\"");
            }
            else
            {
                _scriptAttributes.Add(attributeName, attributeValue);
            }
        }

        protected void RegisterScriptAttribute(string attributeName, string attributeValue)
        {
            RegisterScriptAttribute(attributeName, attributeValue, true);
        }

        string IMvcControl.GetControlText()
        {
            if (string.IsNullOrEmpty(Text)) Text = ErrorMessage;
            if (string.IsNullOrEmpty(Tooltip))
                Tooltip = string.IsNullOrEmpty(ErrorMessage)
                              ? Text
                              : ErrorMessage;
            if (string.IsNullOrEmpty(ErrorElement))
                ErrorElement = "span";
            var tagBuilder = new TagBuilder(ErrorElement);
            tagBuilder.GenerateId(ID);
            tagBuilder.SetInnerText(Text);
            tagBuilder.MergeAttribute("title", Tooltip);
            if (_htmlAttributes != null)
            {
                tagBuilder.MergeAttributes((from attr in _htmlAttributes.GetType().GetProperties()
                                            select new
                                                       {
                                                           attr.Name,
                                                           Value = attr.GetValue(_htmlAttributes, null)
                                                       })
                                               .ToDictionary(obj => obj.Name,
                                                             obj => obj.Value), true);
            }
            ID = tagBuilder.Attributes["id"];
            if ((ErrorElement ?? "").ToLower() == "label")
                tagBuilder.MergeAttribute("for", ControlToValidate, true);

            var _style =
                tagBuilder.Attributes.ContainsKey("style")
                    ? (from attr in tagBuilder.Attributes["style"].Split(';')
                       let attrs = attr.ToLowerInvariant().Split(':')
                       select new {Key = attrs[0].Trim(), Value = attrs[1].Trim()})
                          .ToDictionary(x => x.Key,
                                        x => x.Value)
                    : new Dictionary<string, string>();
            if (!_style.ContainsKey("color")) _style.Add("color", "red");
            switch (Display)
            {
                case ValidatorDisplay.Dynamic:
                case ValidatorDisplay.None:
                    if (_style.ContainsKey("display"))
                        _style["display"] = "none";
                    else
                        _style.Add("display", "none");
                    break;
                case ValidatorDisplay.Static:
                    if (_style.ContainsKey("visibility"))
                        _style["visibility"] = "hidden";
                    else
                        _style.Add("visibility", "hidden");
                    break;
            }
            tagBuilder.MergeAttribute("style",
                                      string.Join(";", (from attr in _style
                                                        select attr.Key + ":" + attr.Value)
                                                           .ToArray()), true);
            return tagBuilder.ToString();
        }

        public string ID { get; internal set; }

        public string ControlToValidate
        {
            get { return _controlToValidate; }
        }

        public string ErrorElement { get; set; }
        public string Text { get; set; }
        public string Tooltip { get; set; }
        public string ErrorMessage { get; set; }
        public bool Enabled { get; set; }
        public ValidatorDisplay Display { get; set; }

        string IMvcControl.GetScript()
        {
            _scriptAttributes.Clear();
            AddScriptAttributes();

            var script = new StringBuilder()
                .AppendLine(
                string.Format("var {0} = document.all ? document.all[\"{0}\"] : document.getElementById(\"{0}\")", ID));
            foreach (var scriptAttribute in _scriptAttributes)
            {
                script.AppendLine(string.Format("{0}.{1} = {2}", ID, scriptAttribute.Key, scriptAttribute.Value));
            }
            return script.ToString();
        }

        protected virtual void AddScriptAttributes()
        {
            if (!string.IsNullOrEmpty(ControlToValidate))
                RegisterScriptAttribute("controltovalidate", ControlToValidate);
            if (!string.IsNullOrEmpty(ErrorMessage))
                RegisterScriptAttribute("errormessage", ErrorMessage);
            if (!string.IsNullOrEmpty(ValidationGroup))
                RegisterScriptAttribute("validationGroup", ValidationGroup);
            if (SetFocusOnError)
                RegisterScriptAttribute("focusOnError", "t");
            if (!Enabled)
                RegisterScriptAttribute("enabled", "False");

            if (Display != ValidatorDisplay.Static)
                RegisterScriptAttribute("display", Display.ToString());
        }

        public bool SetFocusOnError { get; set; }
        public string ValidationGroup { get; set; }
    }
}